#ifndef G3_MEDIA_STREAMER_H
#define G3_MEDIA_STREAMER_H

#include <string>
#include <vector>
#include <set>
#include <boost/signals2/signal.hpp>

#include "multithreading.h"
#include "file/f_stream.h"
#include "timer.h"
#include "log.h"
#include "singleton.h"
#include "gui/gui_typemgr.h"

#define MAX_PRIORITY        10
#define DEF_CHUNK_SIZE      1048576

namespace G3 {

class MEDIA_OBJECT: public LOGGABLE {
    public:
        MEDIA_OBJECT (const MEDIA_OBJECT &obj);
        MEDIA_OBJECT ();
        virtual ~MEDIA_OBJECT ();

        //! Read result enum
        enum READ_RESULT {
            //! Need more chunks
            RR_NEED_MORE = 0,
            //! Done - everything is loaded
            RR_DONE = 1,
            //! Error - failed to load
            RR_ERROR = 2
        };

        //! File where the media object is read from
        std::string filepath;
        //! Stream pointer
        G3::STREAM *stream;
        //! Object priority
        int priority;

        //! Position in the file
        long filepos;
        //! Size of a chunk
        long chunk_size;

		typedef boost::signals2::signal0<void> SIG_LOADED_EVENT;
		typedef boost::signals2::signal1<void, long> SIG_FAILED_EVENT;
		typedef boost::signals2::connection SIG_CONNECTION;

		typedef void (*LOADED_HANDLER)(void);
		typedef void (*FAILED_HANDLER)(int);

		// Signals
		SIG_LOADED_EVENT sig_loaded;
		SIG_FAILED_EVENT sig_failed;

        //! Has it been loaded successfully?
		bool loaded;

		// Connections
		std::vector<SIG_CONNECTION> conn_loaded;
		std::vector<SIG_CONNECTION> conn_failed;

        MEDIA_OBJECT &operator= (const MEDIA_OBJECT &obj);

        // Sets up a callback for a loaded event
		SIG_CONNECTION doOnLoaded (LOADED_HANDLER aLoadedHandler);
        // Sets up a callback for a failed event
		SIG_CONNECTION doOnFailed (FAILED_HANDLER aFailedHandler);

        /**
            @brief Reads the media stream
            @return RR_NEED_MORE, RR_DONE or RR_ERROR
        */
        virtual READ_RESULT read () {
            Assert (false, "Pure virtual function MEDIA_OBJECT::read called..\n");

            return RR_ERROR;
        }

        /**
            @brief Clears the media object
            @return True on success, false on failure
        */
        virtual bool clear () {
            Assert (false, "Pure virtual function MEDIA_OBJECT::clear called..\n");

            return false;
        }

        //! Stream timer
        G3::TIMER timer;

        /**
            @brief Dumps the media object
        */
        virtual void dump () {
            if (log) {
                log->Report ("Dumping MEDIA_OBJECT:\n");
                log->Report (" filepath = \"%s\"\n"
                             " stream = 0x%X\n"
                             " priority = %d\n",
                             filepath.c_str (), stream, priority);
                log->Report (" filepos = %ld\n"
                             " chunk_size = %ld\n",
                             filepos, chunk_size);
            }
        }

        /**
            @brief Gets the type of this widget
            @return The type index \see TYPE_MANAGER
        */
		virtual uint GetType () { return MainTypeMgr.GetType ("R_MEDIA_OBJECT"); }
};

/** @class MEDIA_SORT_FUNCTOR
	@brief A functor for media pointer sorting */
class MEDIA_SORT_FUNCTOR {
    public:
        /**
            @brief Sorting functor
            @param[in] a Pointer to the first media object to be compared
            @param[in] b Pointer to the second media object to be compared
            @return True if a < b and false otherwise
        */
        bool operator() (boost::shared_ptr<MEDIA_OBJECT> a, boost::shared_ptr<MEDIA_OBJECT> b) const;
};

class MEDIA_STREAMER: public THREAD_OBJECT {
    public:
        MEDIA_STREAMER ();
        ~MEDIA_STREAMER ();

        std::set<boost::shared_ptr<MEDIA_OBJECT>, MEDIA_SORT_FUNCTOR> objects;

        /**
            @brief Adds a new media object into the list
            @param[in] obj Pointer to the media object to be added
            @return True on success, false on failure
        */
        bool add (const boost::shared_ptr<MEDIA_OBJECT> &obj);
        /**
            @brief Removes a media object from the list
            @param[in] obj Pointer to the media object to be removed
            @return True on success, false on failure
        */
        bool remove (const boost::shared_ptr<MEDIA_OBJECT> &obj);

        /**
            @brief Clears the objects and shuts down the streamer
        */
        void clear ();

        /** The pending add media objects list */
        std::set<boost::shared_ptr<MEDIA_OBJECT>, MEDIA_SORT_FUNCTOR> pending_add;
        /** The pending remove media objects list */
        std::set<boost::shared_ptr<MEDIA_OBJECT>, MEDIA_SORT_FUNCTOR> pending_remove;

        boost::mutex mutex_objects;

        void process ();

        virtual void dump ();

    private:
        /** Mutex for blocking the pending list */
        boost::mutex mutex_add;
        /** Mutex for blocking the pending remove list */
        boost::mutex mutex_remove;

        /**
            @brief Updates the media objects list by adding the pending objects
        */
        void update_add ();

        /**
            @brief Updates the media objects list by removing the pending objects
        */
        void update_remove ();

        void update ();
};

}

#endif // G3_MEDIA_STREAMER_H
